package com.liusand.user.service.Impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.dysmsapi20170525.AsyncClient;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsResponse;
import com.liusand.common.constans.RedisPrefixCode;
import com.liusand.common.constans.SecretKey;
import com.liusand.common.utils.Utils;
import com.liusand.entity.dto.TwoPhoneDto;
import com.liusand.entity.vo.ListUserVo;
import com.liusand.entity.vo.PageInfo;
import com.liusand.entity.vo.UserVo;
import com.liusand.user.exception.UserOperateException;
import com.liusand.user.thread.UserThreadLocalUtil;
import com.liusand.entity.dto.PhoneAndVerifyCodeDto;
import com.liusand.entity.dto.UserDto;
import com.liusand.entity.pojo.User;
import com.liusand.entity.vo.LoginVo;
import com.liusand.user.config.Sms;
import com.liusand.user.mapper.UserMapper;
import com.liusand.user.service.UserService;
import darabonba.core.client.ClientOverrideConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @author :liusha
 * packageName :com.liusand.user.service.Impl
 * fileName :UserServiceImpl
 * creationTime :2022/12/10 21:24
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private Sms sms;

    @Override
    public boolean sendSms(PhoneAndVerifyCodeDto phone) {
        if (phone == null || StringUtils.isEmpty(phone.getPhone())) {
            return false;
        }
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                .accessKeyId(sms.getAccessKeyId())
                .accessKeySecret(sms.getAccessKeySecret())
                .build());
        AsyncClient client = AsyncClient.builder()
                .region("cn-hangzhou") // Region ID
                .credentialsProvider(provider)
                .overrideConfiguration(
                        ClientOverrideConfiguration.create()
                                .setEndpointOverride("dysmsapi.aliyuncs.com")
                )
                .build();
        Random random = new Random();
        int verifyCode = random.nextInt(900000) + 100000;
        log.info("{}", verifyCode);
        SendSmsRequest sendSmsRequest = SendSmsRequest.builder()
                .signName(sms.getSignName())
                .templateCode(sms.getTemplateCode())
                .phoneNumbers(phone.getPhone())
                .templateParam("{\"code\":\"" + verifyCode + "\"}")
                .build();
        if (true) {
            redisTemplate.opsForValue().set(RedisPrefixCode.VERIFY_CODE_PHONE_KEY + "_" + phone.getPhone(), verifyCode + "", 10, TimeUnit.MINUTES);
            return true;
        }
        CompletableFuture<SendSmsResponse> response = client.sendSms(sendSmsRequest);
        SendSmsResponse resp = null;
        try {
            resp = response.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
        boolean res = "OK".equals(resp.getBody().getCode());
        log.info("{}", resp);
        if (res) {
            redisTemplate.opsForValue().set(RedisPrefixCode.VERIFY_CODE_PHONE_KEY + "_" + phone.getPhone(), verifyCode + "", 10, TimeUnit.MINUTES);
        }
        return res;
    }

    @Override
    public LoginVo login(PhoneAndVerifyCodeDto phoneAndCode) {
        if (phoneAndCode == null || StringUtils.isEmpty(phoneAndCode.getPhone()) || StringUtils.isEmpty(phoneAndCode.getVerifyCode())) {
            throw new RuntimeException("登录失败");
        }
        String phone = phoneAndCode.getPhone();
        String verifyCode = phoneAndCode.getVerifyCode();
        String verifyCodeInRedis = (String) redisTemplate.opsForValue().get(RedisPrefixCode.VERIFY_CODE_PHONE_KEY + "_" + phone);
        if (verifyCodeInRedis == null || !verifyCodeInRedis.equals(verifyCode)) {
            throw new RuntimeException("登录失败");
        }
        User userInDb = userMapper.selectUserByPhone(phone);
        if (userInDb == null) {
            User user = new User();
            user.setId(IdUtil.getSnowflakeNextIdStr());
            user.setPhone(phone);
            boolean insertResult = userMapper.insertUser(user);
            if (!insertResult) {
                throw new RuntimeException("登录失败");
            }
            userInDb = userMapper.selectUserByPhone(phone);
        }
        DateTime now = DateTime.now();
        DateTime newTime = now.offsetNew(DateField.DAY_OF_YEAR, 30);

        log.info("{}", now.offsetNew(DateField.DAY_OF_YEAR, 10));

        Map<String, Object> map = new HashMap<>();
        map.put("id", userInDb.getId());
        map.put("phone", userInDb.getPhone());
        map.put(JWTPayload.ISSUED_AT, now);//签发时间
        map.put(JWTPayload.EXPIRES_AT, newTime);//到期时间
        map.put(JWTPayload.NOT_BEFORE, now);//生效时间
        String token = JWTUtil.createToken(map, SecretKey.JWT_KEY.getBytes());
        LoginVo loginVo = new LoginVo();
        loginVo.setToken(token);
        loginVo.setFirstLogin(false);
        if (StringUtils.isEmpty(userInDb.getNickname())
                || StringUtils.isEmpty(userInDb.getCity())
                || StringUtils.isEmpty(userInDb.getBirthday())
                || userInDb.getGender() == null
                || userInDb.getGender() == 0) {
            loginVo.setFirstLogin(true);
        }
        redisTemplate.delete(RedisPrefixCode.VERIFY_CODE_PHONE_KEY + "_" + phone);
        return loginVo;
    }

    @Override
    public boolean modifyInfo(UserDto userDto) {
        if (userDto == null || StringUtils.isEmpty(userDto.getBirthday())
                || StringUtils.isEmpty(userDto.getCity())
                || StringUtils.isEmpty(userDto.getNickname())
                || userDto.getGender() == null) {
            throw new RuntimeException("更新资料失败");
        }
        User user = new User();
        BeanUtils.copyProperties(userDto, user);
        user.setId(UserThreadLocalUtil.getId());
        user.setUpdateTime(LocalDateTime.now());
        int updateResult = userMapper.updateUser(user);
        if (updateResult != 1) {
            throw new RuntimeException("更新资料失败");
        }
        return true;
    }

    @Override
    public PageInfo<ListUserVo> getList(Integer type, String value, Integer page, Integer pageSize) {
        if(type < 0 || type > 4){
            throw new RuntimeException("类型错误");
        }
        List<ListUserVo> userVoList = new ArrayList<>();
        String id = UserThreadLocalUtil.getId();
        PageInfo<ListUserVo> pageInfo = new PageInfo<>();
        pageInfo.setCurrent(page);
        pageInfo.setPageSize(pageSize);
        page = (page - 1) * pageSize;
        userVoList = userMapper.selectLikeListByUserId(type, id, value, null, page, pageSize);
        if(userVoList == null || userVoList.size() == 0){
            userVoList = userMapper.selectLikeListByUserId(type, id, null, value, page, pageSize);
            pageInfo.setTotal(userMapper.selectCountListByUserId(type, id, null, value));
        }else{
            pageInfo.setTotal(userMapper.selectCountListByUserId(type, id, value, null));

        }
        pageInfo.setInfo(userVoList);
        return pageInfo;
    }

    @Override
    public Long countGetList(Integer type, String value) {
        if(type < 0 || type > 4){
            throw new RuntimeException("类型错误");
        }
        String id = UserThreadLocalUtil.getId();
        Long num = userMapper.selectCountListByUserId(type, id, value, null);
        if(num == 0){
            num = userMapper.selectCountListByUserId(type, id, null, value);
        }
        return num;
    }
    //操作
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean relationHandle(Integer type, String toId) {
        if(type < 0 || type > 5){
            throw new RuntimeException("类型错误");
        }
        String userId = UserThreadLocalUtil.getId();
        Long result = 0L;
        if(type == 0){
            List<String> strings = userMapper.selectDisableId(userId, toId);
            if(strings != null && strings.size() != 0){
                throw new UserOperateException("对方在你的黑名单中或者你在对方黑名单中");
            }
            String followId = userMapper.selectFollowIdByTwoId(userId, toId);
            if(!StringUtils.isEmpty(followId)){
                throw new UserOperateException("禁止重复添加");
            }
            result = userMapper.insertRelation(type, IdUtil.getSnowflakeNextIdStr(), userId, toId);
        }else if(type == 2){
            String disableId = userMapper.selectDisableIdByTwoId(userId, toId);
            if(!StringUtils.isEmpty(disableId)){
                throw new UserOperateException("禁止重复添加");
            }
            userMapper.deleteRelation(1, userId, toId);
            userMapper.deleteRelation(1, toId, userId);

            result = userMapper.insertRelation(type, IdUtil.getSnowflakeNextIdStr(), userId, toId);
        }else if(type == 4){
            List<String> strings = userMapper.selectDisableId(userId, toId);
            if(strings != null && strings.size() != 0){
                throw new UserOperateException("对方在你的黑名单中或者你在对方黑名单中");
            }
            String visitedId = userMapper.selectVisitedIdIdByTwoId(userId, toId);
            if(StringUtils.isEmpty(visitedId)){
                result = userMapper.insertRelation(type, IdUtil.getSnowflakeNextIdStr(), userId, toId);
            }else{
                result = userMapper.updateVisitedTime(userId, toId);
            }
        }else{
            result = userMapper.deleteRelation(type, userId, toId);
        }
        return result == 1;
    }

    @Override
    public User getInfo() {
        String id = UserThreadLocalUtil.getId();
        User user = userMapper.selectUserById(id);
        return user;
    }

    @Override
    public List<UserVo> getVisitedUserVo(Integer page, Integer pageSize) {
        String id = UserThreadLocalUtil.getId();
        page = (page - 1) * pageSize;
        List<UserVo> userVoList = userMapper.selectVisitedUserVoByIds(id, page, pageSize);
        User user = userMapper.selectUserById(id);
        int userAge = Utils.birthday2age(user.getBirthday());
        for(UserVo userVo : userVoList){
            int age = Utils.birthday2age(userVo.getBirthday());
            userVo.setAge(age);
            userVo.setScore(Utils.getScore(userAge, age));
        }
        return userVoList;
    }

    @Override
    public boolean verfyPhone(PhoneAndVerifyCodeDto phoneAndCode) {
        phoneAndCode.setPhone(UserThreadLocalUtil.getPhone());
        String code = redisTemplate.opsForValue().get(RedisPrefixCode.VERIFY_CODE_PHONE_KEY + "_" + phoneAndCode.getPhone());
        if(code == null || !code.equals(phoneAndCode.getVerifyCode())){
            return false;
        }
        return true;
    }

    @Override
    public boolean updatePhone(TwoPhoneDto twoPhoneDto) {
        twoPhoneDto.setOldPhone(UserThreadLocalUtil.getPhone());
        String code = redisTemplate.opsForValue().get(RedisPrefixCode.VERIFY_CODE_PHONE_KEY + "_" + twoPhoneDto.getOldPhone());
        if(code == null || !code.equals(twoPhoneDto.getOldVerifyCode())){
            return false;
        }
        String newCode = redisTemplate.opsForValue().get(RedisPrefixCode.VERIFY_CODE_PHONE_KEY + "_" + twoPhoneDto.getNewPhone());
        if(newCode == null || !newCode.equals(twoPhoneDto.getNewVerifyCode())){
            return false;
        }
        String id = UserThreadLocalUtil.getId();
        int result = userMapper.updatePhoneById(id, twoPhoneDto.getNewPhone());
        if(result != 1){
            throw new RuntimeException("修改手机异常");
        }
        return true;
    }

    @Override
    public List<User> getUserInfo(List<String> userIds) {
        if(CollectionUtils.isEmpty(userIds)){
            return null;
        }
        List<User> userList = userMapper.selectUserByIds(userIds);
        return userList;
    }
}
